home *** CD-ROM | disk | FTP | other *** search
- /*-----------------------------------------------------------------------------
- RDCF: A Reentrant DOS-Compatible File System, Version 2.0
-
- by Philip J. Erdelsky
- PLAIN VANILLA CORPORATION
- CompuServe 75746,3411
- InterNet 75746.3411@compuserve.com
-
- January 15, 1993
-
- Copyright (c) 1993 Plain Vanilla Corporation
- -----------------------------------------------------------------------------*/
-
- /* #define _BIG_ENDIAN */
-
- #include "rdcf2.h"
- #include <mem.h>
- #include <ctype.h>
- #include <stdio.h>
-
- #define NAME_SIZE 8
- #define EXTENSION_SIZE 3
-
- /* Directory structure as it appears on the diskette. */
-
- struct directory
- {
- unsigned char name_extension[NAME_SIZE+EXTENSION_SIZE];
- unsigned char attribute;
- unsigned char reserved[10];
- unsigned short time;
- unsigned short date;
- unsigned short first_cluster;
- unsigned long size;
- };
-
- #ifdef RDCF_SECTOR_SIZE
- #define SECTOR_SIZE RDCF_SECTOR_SIZE
- #else
- #define SECTOR_SIZE f->sector_size
- #endif
-
- #define ENTRIES_PER_SECTOR (SECTOR_SIZE/sizeof(struct directory))
- #define NO_DIRECTORY_INDEX 0xFFFF
-
- /* Special values for first byte of name. */
-
- #define END_DIRECTORY 0x00
- #define DELETED_FILE 0xE5
-
- /* Special values for FAT entries. */
-
- #define EMPTY_CLUSTER 0
- #define RESERVED_CLUSTER_12_BIT 0xFF0
- #define LAST_CLUSTER_12_BIT 0xFF8
- #define LAST_CLUSTER_16_BIT 0xFFF8
-
- /* buffer status */
-
- enum buffer_status {EMPTY, CLEAN, DIRTY};
-
- /* additional mode bit */
-
- #define WRITTEN (1<<7)
-
- /*-----------------------------------------------------------------------------
- Layout of first sector when read into buffer[]. A simple structure would not
- be portable because some words are not aligned on even addresses.
- -----------------------------------------------------------------------------*/
-
- #define BYTES_PER_SECTOR (buffer[11]|buffer[12]<<8)
- #define SECTORS_PER_CLUSTER buffer[13]
- #define RESERVED_SECTORS (buffer[14]|buffer[15]<<8)
- #define NUMBER_OF_FATS buffer[16]
- #define ROOT_ENTRIES (buffer[17]|buffer[18]<<8)
- #define TOTAL_SECTORS (buffer[19]|buffer[20]<<8)
- #define MEDIA_DESCRIPTOR buffer[21]
- #define SECTORS_PER_FAT (buffer[22]|buffer[23]<<8)
- #define SECTORS_PER_TRACK (buffer[24]|buffer[25]<<8)
- #define HEADS (buffer[26]|buffer[27]<<8)
- #define HIDDEN_SECTORS (buffer[28]|buffer[29]<<8)
-
- /*-----------------------------------------------------------------------------
- The following functions and macros convert words and double words from "big
- endian" to "little endian" form or vice-versa.
- -----------------------------------------------------------------------------*/
-
- #ifdef _BIG_ENDIAN
-
- static void swap_two(unsigned char *p)
- {
- unsigned char x = p[0];
- p[0] = p[1];
- p[1] = x;
- }
-
- static void swap_four(unsigned char *p)
- {
- unsigned char x = p[0];
- p[0] = p[3];
- p[3] = x;
- swap_two(p+1);
- }
-
- #define convert_short(x) swap_two((unsigned char *)(&(x)))
- #define convert_long(x) swap_four((unsigned char *)(&(x)))
-
- #endif
-
- /*-----------------------------------------------------------------------------
- This function calls longjmp() to specify an error code and exit from the RDCF
- function originally called.
- -----------------------------------------------------------------------------*/
-
- static void error_exit(struct rdcf *f, int error)
- {
- longjmp(f->error, error);
- }
-
- /*-----------------------------------------------------------------------------
- This function reads or writes a sector and bails out if the function
- (*drive_access)() returns an error.
- -----------------------------------------------------------------------------*/
-
- static void access_sector(struct rdcf *f, int write, unsigned sector,
- void *buffer)
- {
- f->drive_error = (*f->drive_access)(write, f->drive, sector, buffer);
- if (f->drive_error != 0)
- error_exit(f, RDCF_DRIVE_ERROR);
- }
-
- /*-----------------------------------------------------------------------------
- These macros make the calls on access_sector() more readable.
- -----------------------------------------------------------------------------*/
-
- #define read_sector(f,s,b) access_sector(f,0,s,b)
- #define write_sector(f,s,b) access_sector(f,1,s,b)
-
- /*-----------------------------------------------------------------------------
- This function writes the buffer in the FCB if it is marked as "dirty".
- -----------------------------------------------------------------------------*/
-
- #ifndef RDCF_SYSTEM_READ_ONLY
-
- static void flush_buffer(struct rdcf *f)
- {
- if (f->buffer_status == DIRTY)
- {
- write_sector(f, f->sector_in_buffer, f->buffer);
- f->buffer_status = CLEAN;
- }
- }
-
- #endif
-
- /*-----------------------------------------------------------------------------
- This function reads a sector into the buffer in the FCB, if it is not already
- there. If another sector is there, the buffer is first flushed.
- -----------------------------------------------------------------------------*/
-
- static void read_buffer(struct rdcf *f, unsigned sector)
- {
- if (f->buffer_status == EMPTY || sector != f->sector_in_buffer)
- {
- #ifndef RDCF_SYSTEM_READ_ONLY
- flush_buffer(f);
- #endif
- read_sector(f, sector, f->buffer);
- f->sector_in_buffer = sector;
- f->buffer_status = CLEAN;
- }
- }
-
- /*-----------------------------------------------------------------------------
- This function checks to see if a cluster number is valid and declares an error
- if it is not.
- -----------------------------------------------------------------------------*/
-
- static void check_cluster(struct rdcf *f, unsigned cluster)
- {
- if (cluster < 2 || cluster > f->maximum_cluster_number)
- error_exit(f, RDCF_FILE_FORMAT_ERROR);
- }
-
- /*-----------------------------------------------------------------------------
- This function returns the FAT entry for the specified cluster.
- -----------------------------------------------------------------------------*/
-
- static unsigned FAT_entry(struct rdcf *f, unsigned cluster)
- {
- check_cluster(f, cluster);
- if (f->maximum_cluster_number < RESERVED_CLUSTER_12_BIT)
- {
- unsigned byte_index = cluster + (cluster>>1);
- unsigned char p[2];
- read_buffer(f, f->first_FAT_sector + byte_index/SECTOR_SIZE);
- p[0] = f->buffer[byte_index%SECTOR_SIZE];
- byte_index++;
- read_buffer(f, f->first_FAT_sector + byte_index/SECTOR_SIZE);
- p[1] = f->buffer[byte_index%SECTOR_SIZE];
- return cluster&1 ? p[1]<<4 | p[0]>>4 : p[0] | p[1]<<8&0xF00;
- }
- else
- {
- unsigned short x;
- read_buffer(f, f->first_FAT_sector + cluster/(SECTOR_SIZE/2));
- x = ((unsigned short *)(f->buffer))[cluster%(SECTOR_SIZE/2)];
- #ifdef _BIG_ENDIAN
- convert_short(x);
- #endif
- return x;
- }
- }
-
- /*-----------------------------------------------------------------------------
- This function sets the FAT entry for the specified cluster to the specified
- value. The 12-bit FAT entry always occupies two consecutive bytes, filling one
- byte completely and filling only one nibble of the other byte. Since these
- bytes may be in different sectors, two separate calls on read_buffer() are
- used. The one-sector caching implemented by read_buffer() prevents multiple
- disk accesses when both bytes are in the same sector. Every copy of the FAT is
- updated in the same way.
- -----------------------------------------------------------------------------*/
-
- #ifndef RDCF_SYSTEM_READ_ONLY
-
- static void set_FAT_entry(struct rdcf *f, unsigned cluster, unsigned x)
- {
- unsigned sector;
- check_cluster(f, cluster);
- #ifdef _BIG_ENDIAN
- if (f->maximum_cluster_number >= RESERVED_CLUSTER_12_BIT)
- convert_short(x);
- #endif
- for (sector = f->first_FAT_sector; sector < f->first_directory_sector;
- sector += f->sectors_per_FAT)
- {
- if (f->maximum_cluster_number < RESERVED_CLUSTER_12_BIT)
- {
- unsigned byte_index = cluster + (cluster>>1);
- unsigned char *p;
- read_buffer(f, sector + byte_index/SECTOR_SIZE);
- p = f->buffer + byte_index%SECTOR_SIZE;
- *p = cluster&1 ? *p&0x0F | x<<4 : x;
- f->buffer_status = DIRTY;
- read_buffer(f, sector + (byte_index+1)/SECTOR_SIZE);
- p = f->buffer + (byte_index+1)%SECTOR_SIZE;
- *p = cluster&1 ? x>>4 : *p&0xF0 | x>>8;
- }
- else
- {
- read_buffer(f, sector + cluster/(SECTOR_SIZE/2));
- ((unsigned short *)(f->buffer))[cluster%(SECTOR_SIZE/2)] = x;
- }
- f->buffer_status = DIRTY;
- }
- }
-
- #endif
-
- /*-----------------------------------------------------------------------------
- This function checks the value of c (which is always in the range from 0 to
- 255, inclusive). If it represents a character that cannot appear in a valid
- file name or extension, it bails out.
- -----------------------------------------------------------------------------*/
-
- static void check_file_character(struct rdcf *f, unsigned c)
- {
- static unsigned char table[32] =
- {
- 0xFF, 0xFF, 0xFF, 0xFF, 0x05, 0xDC, 0x00, 0xFC,
- 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x90,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
- };
- if (table[c>>3] & 1<<(c&7)) error_exit(f, RDCF_INVALID_SPEC);
- }
-
- /*-----------------------------------------------------------------------------
- This function edits a file or directory spec into the name-extension form used
- in the file directory entry. It returns a pointer to the character
- following the last one of the spec.
- -----------------------------------------------------------------------------*/
-
- static char *spec_to_name_extension(struct rdcf *f,
- unsigned char *name_extension, unsigned char *spec)
- {
- unsigned i = 0;
- unsigned c;
- while ((c=(*spec++))!=0 && c!='\\' && c!='.')
- {
- check_file_character(f,c);
- if (i<NAME_SIZE) name_extension[i++] = toupper(c);
- }
- if (i==0) error_exit(f, RDCF_INVALID_SPEC);
- while (i<NAME_SIZE) name_extension[i++] = ' ';
- if (c=='.') while ((c=(*spec++))!=0 && c!='\\')
- {
- check_file_character(f,c);
- if (i<NAME_SIZE+EXTENSION_SIZE) name_extension[i++] = toupper(c);
- }
- while (i<NAME_SIZE+EXTENSION_SIZE) name_extension[i++] = ' ';
- return spec-1;
- }
-
- /*-----------------------------------------------------------------------------
- This function edits the name-extension form used in a file entry to file spec.
- -----------------------------------------------------------------------------*/
-
- static void name_extension_to_spec(unsigned char *spec,
- unsigned char *name_extension)
- {
- unsigned i;
- unsigned char *s = spec;
- for (i=0; i<NAME_SIZE && name_extension[i]!=' '; i++)
- *s++ = name_extension[i];
- if (name_extension[NAME_SIZE]!=' ')
- {
- *s++ = '.';
- for (i=NAME_SIZE;
- i<NAME_SIZE+EXTENSION_SIZE && name_extension[i]!=' '; i++)
- {
- *s++ = name_extension[i];
- }
- }
- *s = 0;
- }
-
- /*-----------------------------------------------------------------------------
- This function calculates the number of the first sector in the specified
- cluster.
- -----------------------------------------------------------------------------*/
-
- static unsigned first_sector_in_cluster(struct rdcf *f, unsigned cluster)
- {
- check_cluster(f, cluster);
- return f->first_data_sector + (cluster-2) * f->sectors_per_cluster;
- }
-
- /*-----------------------------------------------------------------------------
- This function finds the directory entry referred to by f->directory_cluster and
- f->directory_index, reads it into f->buffer, and returns a pointer to it.
- -----------------------------------------------------------------------------*/
-
- static struct directory *find_directory(struct rdcf *f)
- {
- read_buffer(f, (f->directory_cluster == 0 ? f->first_directory_sector :
- first_sector_in_cluster(f, f->directory_cluster)) +
- f->directory_index/ENTRIES_PER_SECTOR);
- return
- (struct directory *)(f->buffer) + f->directory_index%ENTRIES_PER_SECTOR;
- }
-
- /*-----------------------------------------------------------------------------
- This function updates a directory entry. If the "delete_entry" parameter is
- true (nonzero), it also marks the entry as deleted.
- -----------------------------------------------------------------------------*/
-
- #ifndef RDCF_SYSTEM_READ_ONLY
-
- static void update_directory_entry(struct rdcf *f, int delete_entry)
- {
- unsigned i;
- struct directory *d = find_directory(f);
- if (f->file.attribute & RDCF_VOLUME || f->file.spec[0] == '.')
- memcpy(d->name_extension, f->file.spec, NAME_SIZE+EXTENSION_SIZE);
- else
- spec_to_name_extension(f, d->name_extension, f->file.spec);
- if (delete_entry) d->name_extension[0] = DELETED_FILE;
- d->attribute = f->file.attribute;
- d->date = (f->file.date_and_time.year-1980)<<9 |
- f->file.date_and_time.month<<5 | f->file.date_and_time.day;
- d->time = f->file.date_and_time.hour<<11 |
- f->file.date_and_time.minute<<5 | f->file.date_and_time.second>>1;
- d->first_cluster = f->file.first_cluster;
- d->size = f->file.size;
- memset(d->reserved, 0, sizeof(d->reserved));
- #ifdef _BIG_ENDIAN
- convert_short(d->date);
- convert_short(d->time);
- convert_short(d->first_cluster);
- convert_long(d->size);
- #endif
- f->buffer_status = DIRTY;
- }
-
- #endif
-
- /*-----------------------------------------------------------------------------
- This function reads directory information into an FCB.
- -----------------------------------------------------------------------------*/
-
- static void read_directory_entry(struct rdcf *f)
- {
- struct directory *d = find_directory(f);
- if (d->attribute&RDCF_VOLUME)
- {
- memcpy(f->file.spec, d->name_extension, NAME_SIZE+EXTENSION_SIZE);
- f->file.spec[NAME_SIZE+EXTENSION_SIZE] = 0;
- }
- else
- name_extension_to_spec(f->file.spec, d->name_extension);
- f->file.attribute = d->attribute;
- {
- unsigned short date = d->date;
- #ifdef _BIG_ENDIAN
- convert_short(date);
- #endif
- f->file.date_and_time.year = (date>>9)+1980;
- f->file.date_and_time.month = date>>5&0xF;
- f->file.date_and_time.day = date&0x1F;
- }
- {
- unsigned short time = d->time;
- #ifdef _BIG_ENDIAN
- convert_short(time);
- #endif
- f->file.date_and_time.hour = time>>11;
- f->file.date_and_time.minute = time>>5&0x3F;
- f->file.date_and_time.second = time<<1&0x1F<<1;
- }
- f->file.first_cluster = d->first_cluster;
- f->file.size = d->size;
- #ifdef _BIG_ENDIAN
- convert_short(f->file.first_cluster);
- convert_long(f->file.size);
- #endif
- }
-
- /*-----------------------------------------------------------------------------
- If the parameter "name_extension" is not NULL, this function looks up the name
- and extension in the directory specified by f->directory_cluster (the entire
- root directory if f->directory_cluster == 0). If found, it puts the appropriate
- information into the file structure and returns a true (nonzero) result. If not
- found, it returns a false (zero) value and also puts the cluster and index of
- the first empty entry, if any, into f->directory_cluster and
- f->directory_index. In any case, the name and extension are left in
- f->file.spec, and the first directory cluster is left in
- f->directory_first_cluster.
-
- If the parameter name_extension is NULL, this function looks up the volume
- label in the same way (although in this case the calling function looks only in
- the root directory, of course).
-
- If the file or volume label is not found and there is no empty entry, the
- special value NO_DIRECTORY_INDEX is left in f->directory_index.
- -----------------------------------------------------------------------------*/
-
- static int find_file_in_directory_or_find_volume(struct rdcf *f,
- unsigned char *name_extension)
- {
- unsigned empty_cluster;
- unsigned empty_index = NO_DIRECTORY_INDEX;
- unsigned number_of_directory_entries = f->directory_cluster == 0 ?
- (f->first_data_sector - f->first_directory_sector)*ENTRIES_PER_SECTOR :
- ENTRIES_PER_SECTOR*f->sectors_per_cluster;
- f->directory_first_cluster = f->directory_cluster;
- while (1)
- {
- for (f->directory_index = 0;
- f->directory_index < number_of_directory_entries; f->directory_index++)
- {
- unsigned j;
- struct directory *d = find_directory(f);
- if ((d->name_extension[0] == DELETED_FILE ||
- d->name_extension[0] == END_DIRECTORY) &&
- empty_index == NO_DIRECTORY_INDEX)
- {
- empty_cluster = f->directory_cluster;
- empty_index = f->directory_index;
- }
- if (d->name_extension[0] == END_DIRECTORY) break;
- if (name_extension == NULL &&
- (d->name_extension[0] != DELETED_FILE && d->attribute & RDCF_VOLUME) ||
- name_extension != NULL &&
- (memcmp(d->name_extension, name_extension, NAME_SIZE+EXTENSION_SIZE)
- == 0 && (d->attribute&RDCF_VOLUME) == 0))
- {
- read_directory_entry(f);
- return 1;
- }
- }
- if (f->directory_index < number_of_directory_entries ||
- f->directory_cluster == 0)
- {
- break;
- }
- {
- unsigned x = FAT_entry(f, f->directory_cluster);
- if (x >= f->last_cluster_mark) break;
- f->directory_cluster = x;
- }
- }
- f->directory_index = empty_index;
- if (f->directory_index != NO_DIRECTORY_INDEX)
- f->directory_cluster = empty_cluster;
- if (name_extension != NULL)
- name_extension_to_spec(f->file.spec, name_extension);
- return 0;
- }
-
- /*-----------------------------------------------------------------------------
- This function follows the directory path and finds the directory entry for the
- file (or directory) with the spec provided. If found, it reads the directory
- information into the FCB and returns a true (nonzero) value. Otherwise, it
- returns a false (zero) value and also puts the cluster and index of the first
- available directory entry, if any, into f->directory_cluster and
- f->directory_index. If there is no available directory entry, it puts the
- special value NO_DIRECTORY_INDEX into f->directory_index. In any case, the
- number of the first cluster of the directory is left in
- f->directory_first_cluster and the name and extension are left in f->file.spec.
- -----------------------------------------------------------------------------*/
-
- static int find_file(struct rdcf *f, char *spec)
- {
- /* start with root directory */
- f->directory_cluster = 0;
- while (1)
- {
- int found;
- unsigned char name_extension[NAME_SIZE+EXTENSION_SIZE];
- /* scan name and extension */
- spec = spec_to_name_extension(f, name_extension, spec);
- /* look it up in directory */
- found = find_file_in_directory_or_find_volume(f, name_extension);
- /* if this is the end of the file specification, return */
- if (*spec == 0) return found;
- /* otherwise, the name and extension were a subdirectory in the path */
- if (!found || (f->file.attribute&RDCF_DIRECTORY) == 0)
- error_exit(f, RDCF_INVALID_DIRECTORY);
- f->directory_cluster = f->file.first_cluster;
- /* skip over the \ after the subdirectory */
- spec++;
- }
- }
-
- /*-----------------------------------------------------------------------------
- This function reads the file system information from the first sector.
- -----------------------------------------------------------------------------*/
-
- static void read_file_system_information(struct rdcf *f)
- {
- unsigned total_sectors;
- unsigned i;
- unsigned char *buffer = f->buffer;
- read_buffer(f, 0);
- /* make a perfunctory sanity check to prevent later division by zero */
- if ((total_sectors = TOTAL_SECTORS) == 0 ||
- #ifdef RDCF_SECTOR_SIZE
- BYTES_PER_SECTOR != SECTOR_SIZE ||
- #else
- (SECTOR_SIZE = BYTES_PER_SECTOR) == 0 ||
- #endif
- (f->first_FAT_sector = RESERVED_SECTORS) == 0 ||
- (f->sectors_per_FAT = SECTORS_PER_FAT) == 0 ||
- (f->sectors_per_cluster = SECTORS_PER_CLUSTER) == 0)
- {
- error_exit(f, RDCF_DISK_FORMAT_ERROR);
- }
- f->first_directory_sector =
- f->first_FAT_sector + f->sectors_per_FAT * NUMBER_OF_FATS;
- f->first_data_sector = f->first_directory_sector +
- ROOT_ENTRIES/ENTRIES_PER_SECTOR;
- f->maximum_cluster_number =
- (total_sectors-f->first_data_sector) / SECTORS_PER_CLUSTER + 1;
- f->last_cluster_mark =
- f->maximum_cluster_number < RESERVED_CLUSTER_12_BIT ?
- LAST_CLUSTER_12_BIT : LAST_CLUSTER_16_BIT;
- }
-
- /*-----------------------------------------------------------------------------
- This function gets the drive specifications and returns a pointer to the
- character following the drive specifications.
- -----------------------------------------------------------------------------*/
-
- #ifdef RDCF_MULTIPLE_DRIVE
-
- static char *get_drive(struct rdcf *f, char *spec)
- {
- if (spec[0] != 0 && spec[1] == ':')
- {
- if (isalpha(spec[0])) f->drive = toupper(spec[0])-'A';
- else error_exit(f, RDCF_INVALID_SPEC);
- return spec + 2;
- }
- error_exit(f, RDCF_INVALID_SPEC);
- }
-
- #endif
-
- /*-----------------------------------------------------------------------------
- This function scans the file spec and sets up the file control block for
- further file operations. It returns a pointer to the character following the
- drive specifications.
- -----------------------------------------------------------------------------*/
-
- static char *initialize_fcb(struct rdcf *f, char *spec)
- {
- #ifdef RDCF_MULTIPLE_DRIVE
- spec = get_drive(f, spec);
- #endif
- f->buffer_status = EMPTY;
- read_file_system_information(f);
- return spec;
- }
-
- /*-----------------------------------------------------------------------------
- This function checks write access and generates an error if write access is
- denied.
- -----------------------------------------------------------------------------*/
-
- #ifndef RDCF_SYSTEM_READ_ONLY
-
- static void check_write_access(struct rdcf *f)
- {
- if (f->file.attribute & (RDCF_READ_ONLY+RDCF_HIDDEN+RDCF_SYSTEM))
- error_exit(f, RDCF_ACCESS_DENIED);
- }
-
- #endif
-
- /*-----------------------------------------------------------------------------
- This function releases all the FAT entries of a file.
- -----------------------------------------------------------------------------*/
-
- #ifndef RDCF_SYSTEM_READ_ONLY
-
- static void release_FAT_entries(struct rdcf *f)
- {
- unsigned j;
- j = f->file.first_cluster;
- if (j != EMPTY_CLUSTER)
- {
- while (j < f->last_cluster_mark)
- {
- unsigned k = FAT_entry(f,j);
- set_FAT_entry(f,j,EMPTY_CLUSTER);
- j = k;
- }
- }
- f->mode |= WRITTEN;
- }
-
- #endif
-
- /*-----------------------------------------------------------------------------
- This function finds a new cluster, if possible, and adds it to a chain ending
- with the specified cluster. It returns the new cluster number, or
- EMPTY_CLUSTER if there are no free clusters.
- -----------------------------------------------------------------------------*/
-
- static unsigned add_new_cluster(struct rdcf *f, unsigned cluster,
- unsigned first_possibly_empty_cluster)
- {
- unsigned new_cluster;
- for (new_cluster = first_possibly_empty_cluster;
- new_cluster <= f->maximum_cluster_number; new_cluster++)
- {
- if (FAT_entry(f, new_cluster)==EMPTY_CLUSTER) break;
- }
- if (new_cluster > f->maximum_cluster_number) return EMPTY_CLUSTER;
- if (cluster != EMPTY_CLUSTER) set_FAT_entry(f, cluster, new_cluster);
- set_FAT_entry(f, new_cluster, f->last_cluster_mark);
- return new_cluster;
- }
-
- /*-----------------------------------------------------------------------------
- This function writes zeros into a cluster.
- -----------------------------------------------------------------------------*/
-
- #ifndef RDCF_SYSTEM_READ_ONLY
-
- static void clear_cluster(struct rdcf *f, unsigned cluster)
- {
- unsigned count = f->sectors_per_cluster;
- unsigned sector = first_sector_in_cluster(f, cluster);
- flush_buffer(f);
- f->buffer_status = EMPTY;
- memset(f->buffer, 0, SECTOR_SIZE);
- do write_sector(f, sector++, f->buffer); while (--count != 0);
- }
-
- #endif
-
- /*-----------------------------------------------------------------------------
- This function adds another cluster to the directory if necessary to accommodate
- a new file or subdirectory.
- -----------------------------------------------------------------------------*/
-
- #ifndef RDCF_SYSTEM_READ_ONLY
-
- static void lengthen_directory_if_necessary(struct rdcf *f)
- {
- if (f->directory_index == NO_DIRECTORY_INDEX)
- {
- if (f->directory_cluster == 0)
- error_exit(f, RDCF_DIRECTORY_FULL);
- f->directory_cluster = add_new_cluster(f, f->directory_cluster, 2);
- if (f->directory_cluster == 0)
- error_exit(f, RDCF_DIRECTORY_FULL);
- f->directory_index = 0;
- clear_cluster(f, f->directory_cluster);
- }
- }
-
- #endif
-
- /*-----------------------------------------------------------------------------
- When f represents a subdirectory, this function updates the . and .. entries at
- the beginning of its first cluster.
- -----------------------------------------------------------------------------*/
-
- #ifndef RDCF_SYSTEM_READ_ONLY
-
- static void update_dot_and_dot_dot(struct rdcf *f)
- {
- f->directory_cluster = f->file.first_cluster;
- f->directory_index = 0;
- memset(f->file.spec, ' ', NAME_SIZE+EXTENSION_SIZE);
- f->file.spec[0] = '.';
- update_directory_entry(f, 0);
- f->file.first_cluster = f->directory_first_cluster;
- f->directory_index = 1;
- f->file.spec[1] = '.';
- update_directory_entry(f, 0);
- }
-
- #endif
-
- /*-----------------------------------------------------------------------------
- This function contains the common code from rdcf_get_file_information() and
- rdcf_set_file_information(). It finds the directory entry specified by the
- spec and index, and puts its cluster and index into f->directory_cluster and
- f->directory_index.
- -----------------------------------------------------------------------------*/
-
- static void find_entry(struct rdcf *f, char *spec, unsigned index)
- {
- spec = initialize_fcb(f, spec);
- if (*spec == 0)
- {
- if (index >=
- (f->first_data_sector - f->first_directory_sector)*ENTRIES_PER_SECTOR)
- {
- error_exit(f, RDCF_DIRECTORY_FULL);
- }
- f->directory_first_cluster = f->directory_cluster = 0;
- }
- else
- {
- if (!find_file(f, spec) || (f->file.attribute&RDCF_DIRECTORY) == 0)
- error_exit(f, RDCF_INVALID_DIRECTORY);
- f->directory_first_cluster = f->directory_cluster = f->file.first_cluster;
- while (index >= ENTRIES_PER_SECTOR*f->sectors_per_cluster)
- {
- f->directory_cluster = FAT_entry(f, f->directory_cluster);
- if (f->directory_cluster >= f->last_cluster_mark)
- error_exit(f,RDCF_DIRECTORY_FULL);
- index -= ENTRIES_PER_SECTOR*f->sectors_per_cluster;
- }
- }
- f->directory_index = index;
- }
-
- /*-----------------------------------------------------------------------------
- This function is called by rdcf_sort_directory() to determine the class of a
- directory entry.
- -----------------------------------------------------------------------------*/
-
- static int entry_class(struct directory *d)
- {
- enum {VOLUME_ENTRY, DIRECTORY_ENTRY, FILE_ENTRY, ERASED_ENTRY};
- return d->name_extension[0] == DELETED_FILE ? ERASED_ENTRY :
- d->attribute & RDCF_DIRECTORY ? DIRECTORY_ENTRY :
- d->attribute & RDCF_VOLUME ? VOLUME_ENTRY : FILE_ENTRY;
- }
-
- /*-----------------------------------------------------------------------------
- This function is called by rdcf_sort_directory() to compare two four-byte
- fields.
- -----------------------------------------------------------------------------*/
-
- static int compare_four_bytes(unsigned char *p, unsigned char *q)
- {
- int i, n;
- for (i=3; i>=0 && (n=p[i]-q[i]) == 0; i--);
- return n;
- }
-
- /*-----------------------------------------------------------------------------
- The following functions are publicly defined. They do not call each other and
- any of them may be removed without making changes to those that remain. The
- functions are defined in alphabetical order.
- -----------------------------------------------------------------------------*/
-
- #ifndef RDCF_SYSTEM_READ_ONLY
-
- #define CHANGEABLE_ATTRIBUTES \
- (RDCF_ARCHIVE+RDCF_HIDDEN+RDCF_READ_ONLY+RDCF_SYSTEM)
-
- int rdcf_attribute(struct rdcf *f, char *spec, unsigned attribute)
- {
- if ((f->result=setjmp(f->error)) != 0) return f->result;
- if (!find_file(f, initialize_fcb(f, spec)) ||
- f->file.attribute & RDCF_DIRECTORY)
- {
- error_exit(f, RDCF_FILE_NOT_FOUND);
- }
- f->file.attribute = f->file.attribute & ~CHANGEABLE_ATTRIBUTES |
- attribute & CHANGEABLE_ATTRIBUTES;
- update_directory_entry(f, 0);
- flush_buffer(f);
- return 0;
- }
-
- #endif
-
- #ifndef RDCF_SYSTEM_READ_ONLY
-
- int rdcf_close(struct rdcf *f)
- {
- if ((f->result=setjmp(f->error)) != 0) return f->result;
- if (f->mode&WRITTEN)
- {
- f->buffer_status = EMPTY;
- f->file.attribute |= RDCF_ARCHIVE;
- rdcf_get_date_and_time(&f->file.date_and_time);
- update_directory_entry(f, 0);
- flush_buffer(f);
- }
- return 0;
- }
-
- #endif
-
- #ifndef RDCF_SYSTEM_READ_ONLY
-
- int rdcf_date_and_time(struct rdcf *f, char *spec,
- struct rdcf_date_and_time *p)
- {
- if ((f->result=setjmp(f->error)) != 0) return f->result;
- spec = initialize_fcb(f, spec);
- if (*spec == 0)
- {
- f->directory_cluster = 0;
- if (!find_file_in_directory_or_find_volume(f, NULL))
- error_exit(f, RDCF_FILE_NOT_FOUND);
- }
- else
- {
- if (!find_file(f, spec)) error_exit(f, RDCF_FILE_NOT_FOUND);
- }
- f->file.date_and_time = *p;
- if ((f->file.attribute & RDCF_DIRECTORY+RDCF_VOLUME) == 0)
- {
- check_write_access(f);
- f->file.attribute |= RDCF_ARCHIVE;
- }
- update_directory_entry(f, 0);
- if (f->file.attribute & RDCF_DIRECTORY) update_dot_and_dot_dot(f);
- flush_buffer(f);
- return 0;
- }
-
- #endif
-
- #ifndef RDCF_SYSTEM_READ_ONLY
-
- int rdcf_delete(struct rdcf *f, char *spec)
- {
- if ((f->result=setjmp(f->error)) != 0) return f->result;
- spec = initialize_fcb(f, spec);
- if (*spec == 0) /* delete volume label */
- {
- if (!find_file_in_directory_or_find_volume(f, NULL))
- error_exit(f, RDCF_FILE_NOT_FOUND);
- }
- else if (!find_file(f, spec))
- error_exit(f, RDCF_FILE_NOT_FOUND);
- /* check to see that a directory is empty before deleting it */
- else if (f->file.attribute & RDCF_DIRECTORY)
- {
- unsigned cluster = f->file.first_cluster;
- while (cluster != EMPTY_CLUSTER && cluster < f->last_cluster_mark)
- {
- unsigned sector = first_sector_in_cluster(f, cluster);
- unsigned sector_count = f->sectors_per_cluster;
- unsigned next_cluster = FAT_entry(f, cluster);
- do
- {
- unsigned entry_count = ENTRIES_PER_SECTOR;
- unsigned char *p = f->buffer;
- read_buffer(f, sector);
- do
- {
- unsigned c = *p;
- if (c == END_DIRECTORY) break;
- if (c != DELETED_FILE && c != '.')
- error_exit(f, RDCF_ACCESS_DENIED);
- p += sizeof(struct directory);
- } while (--entry_count != 0);
- if (entry_count != 0) break;
- } while (--sector_count != 0);
- cluster = next_cluster;
- }
- }
- else
- check_write_access(f);
- release_FAT_entries(f);
- update_directory_entry(f, 1);
- flush_buffer(f);
- return 0;
- }
-
- #endif
-
- #ifndef RDCF_SYSTEM_READ_ONLY
-
- int rdcf_directory(struct rdcf *f, char *spec)
- {
- /* unsigned char name_extension[NAME_SIZE+EXTENSION_SIZE]; ??? */
- if ((f->result=setjmp(f->error)) != 0) return f->result;
- if (find_file(f, initialize_fcb(f, spec)))
- error_exit(f,RDCF_DIRECTORY_CONFLICT);
- /* spec_to_name_extension(f, name_extension, f->file.spec); ??? */
- /* name_extension_to_spec(f->file.spec, name_extension); ??? */
- /* determine whether there is enough free space for directory */
- {
- unsigned cluster = 2;
- unsigned required_clusters =
- f->directory_index == NO_DIRECTORY_INDEX ? 2 : 1;
- for (cluster = 2; required_clusters != 0; cluster++)
- {
- if (cluster > f->maximum_cluster_number)
- error_exit(f,RDCF_DIRECTORY_FULL);
- if (FAT_entry(f, cluster)==EMPTY_CLUSTER) required_clusters--;
- }
- }
- lengthen_directory_if_necessary(f);
- f->file.attribute = RDCF_DIRECTORY;
- f->file.first_cluster = add_new_cluster(f, EMPTY_CLUSTER, 2);
- clear_cluster(f, f->file.first_cluster);
- f->file.size = 0L;
- rdcf_get_date_and_time(&f->file.date_and_time);
- update_directory_entry(f, 0);
- update_dot_and_dot_dot(f);
- flush_buffer(f);
- return 0;
- }
-
- #endif
-
- #ifndef RDCF_SYSTEM_READ_ONLY
-
- struct rdcf_format rdcf_format_360 =
- {
- 0,2,2,1,0,2,112,0,208,2,253,2,0,9,0,2,0,0,0
- };
-
- struct rdcf_format rdcf_format_720 =
- {
- 0,2,2,1,0,2,112,0,160,5,249,3,0,9,0,2,0,0,0
- };
-
- struct rdcf_format rdcf_format_1200 =
- {
- 0,2,1,1,0,2,224,0,96,9,249,7,0,15,0,2,0,0,0
- };
-
- struct rdcf_format rdcf_format_1440 =
- {
- 0,2,1,1,0,2,224,0,64,11,240,9,0,18,0,2,0,0,0
- };
-
- int rdcf_format(struct rdcf *f,
- #ifdef RDCF_MULTIPLE_DRIVE
- char *spec,
- #endif
- struct rdcf_format *format)
- {
- static char bootstrap_header[] =
- {
- 235,254,144,'R','D','C','F','2','0',' ',' '
- };
- unsigned char *buffer = f->buffer;
- unsigned char media_descriptor;
- if ((f->result=setjmp(f->error)) != 0) return f->result;
- #ifdef RDCF_MULTIPLE_DRIVE
- if (*get_drive(f, spec) != 0) error_exit(f, RDCF_INVALID_SPEC);
- #endif
- memset(buffer, 0, SECTOR_SIZE);
- memcpy(buffer, bootstrap_header, sizeof(bootstrap_header));
- memcpy(buffer + sizeof(bootstrap_header), format,
- sizeof(struct rdcf_format));
- buffer[SECTOR_SIZE-2] = 0x55;
- buffer[SECTOR_SIZE-1] = 0xAA;
- write_sector(f, 0, buffer);
- f->buffer_status = CLEAN;
- f->sector_in_buffer = 0;
- read_file_system_information(f);
- media_descriptor = MEDIA_DESCRIPTOR;
- memset(buffer, 0, SECTOR_SIZE);
- {
- unsigned sector = f->first_FAT_sector;
- while (sector < f->first_directory_sector)
- {
- unsigned count = f->sectors_per_FAT;
- buffer[0] = media_descriptor;
- buffer[1] = buffer[2] = 0xFF;
- write_sector(f, sector++, buffer);
- buffer[0] = buffer[1] = buffer[2] = 0;
- while (--count != 0)
- write_sector(f, sector++, buffer);
- }
- buffer[0] = buffer[1] = buffer[2] = 0;
- while (sector < f->first_data_sector)
- write_sector(f, sector++, buffer);
- }
- return 0;
- }
-
- #endif
-
- long int rdcf_free_space(struct rdcf *f
- #ifdef RDCF_MULTIPLE_DRIVE
- , char *spec
- #endif
- )
- {
- unsigned cluster;
- unsigned number_of_empty_clusters = 0;
- if ((f->result=setjmp(f->error)) != 0) return (long)(f->result);
- #ifndef RDCF_MULTIPLE_DRIVE
- initialize_fcb(f, NULL);
- #else
- if (*initialize_fcb(f, spec) != 0) error_exit(f, RDCF_INVALID_SPEC);
- #endif
- for (cluster = 2; cluster <= f->maximum_cluster_number; cluster++)
- {
- if (FAT_entry(f, cluster) == EMPTY_CLUSTER) number_of_empty_clusters++;
- }
- f->file.size = (unsigned long) number_of_empty_clusters *
- (f->sectors_per_cluster * SECTOR_SIZE);
- return (long)(f->file.size);
- }
-
- int rdcf_get_file_information(struct rdcf *f, char *spec, unsigned index)
- {
- if ((f->result=setjmp(f->error)) != 0) return f->result;
- find_entry(f, spec, index);
- read_directory_entry(f);
- return f->result = f->file.spec[0] == DELETED_FILE ? RDCF_FILE_NOT_FOUND :
- f->file.spec[0] == 0 ? RDCF_DIRECTORY_FULL : 0;
- }
-
- int rdcf_get_volume(struct rdcf *f
- #ifdef RDCF_MULTIPLE_DRIVE
- , char *spec
- #endif
- )
- {
- if ((f->result=setjmp(f->error)) != 0) return f->result;
- #ifndef RDCF_MULTIPLE_DRIVE
- initialize_fcb(f, NULL);
- #else
- if (*initialize_fcb(f, spec) != 0) error_exit(f, RDCF_INVALID_SPEC);
- #endif
- f->directory_cluster = 0;
- if (!find_file_in_directory_or_find_volume(f, NULL))
- error_exit(f, RDCF_FILE_NOT_FOUND);
- return 0;
- }
-
- int rdcf_match(char *specs, char *pattern)
- {
- unsigned size = NAME_SIZE;
- while (1)
- {
- unsigned count = size;
- do
- {
- int c1, c2;
- if (*specs == '.' || *specs == 0) c1 = ' ';
- else c1 = *specs++;
- if (*pattern == '*') c2 = '?';
- else if (*pattern == '.' || *pattern == 0) c2 = ' ';
- else c2 = *pattern++;
- if (c2 != '?' && toupper(c1) != toupper(c2)) return 0;
- } while (--count != 0);
- while (*specs != '.' && *specs != 0) specs++;
- if (*specs == '.') ++specs;
- if (*pattern == '*') while (*++pattern != '.' && *pattern != 0);
- if (*pattern == '.') ++pattern;
- if (size == NAME_SIZE) size = EXTENSION_SIZE;
- else return 1;
- }
- }
-
- #ifndef RDCF_SYSTEM_READ_ONLY
-
- int rdcf_move(struct rdcf *f, char *old_spec, char *new_spec)
- {
- unsigned char spec[13];
- unsigned short first_cluster;
- unsigned char attribute;
- struct rdcf_date_and_time date_and_time;
- unsigned long size;
- unsigned short directory_cluster;
- unsigned short directory_index;
- if ((f->result=setjmp(f->error)) != 0) return f->result;
- if (!find_file(f, initialize_fcb(f, old_spec)))
- error_exit(f, RDCF_FILE_NOT_FOUND);
- /* save all needed information about old spec */
- memcpy(spec, f->file.spec, sizeof(spec));
- attribute = f->file.attribute;
- date_and_time = f->file.date_and_time;
- size = f->file.size;
- first_cluster = f->file.first_cluster;
- directory_cluster = f->directory_cluster;
- directory_index = f->directory_index;
- if (find_file(f, new_spec)) error_exit(f, RDCF_RENAMING_ERROR);
- f->file.first_cluster = first_cluster;
- f->file.attribute = attribute;
- f->file.date_and_time = date_and_time;
- f->file.size = size;
- lengthen_directory_if_necessary(f);
- update_directory_entry(f, 0);
- if (f->file.attribute & RDCF_DIRECTORY)
- update_dot_and_dot_dot(f);
- memcpy(f->file.spec, spec, sizeof(spec));
- f->directory_cluster = directory_cluster;
- f->directory_index = directory_index;
- update_directory_entry(f, 1);
- flush_buffer(f);
- return 0;
- }
-
-
- #endif
-
- int rdcf_next_file_information(struct rdcf *f)
- {
- if ((f->result=setjmp(f->error)) != 0) return f->result;
- f->directory_index++;
- if (f->directory_cluster == 0)
- {
- if (f->directory_index >=
- (f->first_data_sector - f->first_directory_sector)*ENTRIES_PER_SECTOR)
- {
- error_exit(f, RDCF_DIRECTORY_FULL);
- }
- }
- else
- {
- if (f->directory_index >= ENTRIES_PER_SECTOR*f->sectors_per_cluster)
- {
- f->directory_cluster = FAT_entry(f, f->directory_cluster);
- if (f->directory_cluster >= f->last_cluster_mark)
- error_exit(f,RDCF_DIRECTORY_FULL);
- f->directory_index = 0;
- }
- }
- read_directory_entry(f);
- return f->result = f->file.spec[0] == DELETED_FILE ? RDCF_FILE_NOT_FOUND :
- f->file.spec[0] == 0 ? RDCF_DIRECTORY_FULL : 0;
- }
-
-
- int rdcf_open(struct rdcf *f, char *spec
- #ifndef RDCF_SYSTEM_READ_ONLY
- , unsigned mode
- #endif
- )
- {
- int found;
- if ((f->result=setjmp(f->error)) != 0) return f->result;
- #ifndef RDCF_SYSTEM_READ_ONLY
- f->mode = mode;
- #endif
- found = find_file(f, initialize_fcb(f, spec));
- if (found && f->file.attribute&RDCF_DIRECTORY)
- error_exit(f,RDCF_DIRECTORY_CONFLICT);
- #ifdef RDCF_SYSTEM_READ_ONLY
- if (!found) error_exit(f, RDCF_FILE_NOT_FOUND);
- #else
- switch (mode)
- {
- case RDCF_READ_WRITE:
- check_write_access(f);
- /* fall through to case RDCF_READ */
- case RDCF_READ:
- if (!found)
- error_exit(f,RDCF_FILE_NOT_FOUND);
- break;
- case RDCF_CREATE:
- if (found)
- {
- check_write_access(f);
- release_FAT_entries(f);
- }
- else
- lengthen_directory_if_necessary(f);
- f->file.attribute = RDCF_ARCHIVE;
- f->file.first_cluster = EMPTY_CLUSTER;
- f->file.size = 0L;
- rdcf_get_date_and_time(&f->file.date_and_time);
- f->mode |= WRITTEN;
- update_directory_entry(f, 0);
- flush_buffer(f);
- break;
- default:
- error_exit(f,RDCF_MODE_ERROR);
- }
- #endif
- f->position = 0;
- f->last_cluster = EMPTY_CLUSTER;
- f->cluster = f->file.first_cluster;
- return 0;
- }
-
- int rdcf_read(struct rdcf *f, void *buf, int count)
- {
- unsigned long size = f->file.size;
- unsigned unread_bytes = count;
- unsigned long position = f->position;
- char *buffer = buf;
- if ((f->result=setjmp(f->error)) != 0) return f->result;
- #ifndef RDCF_SYSTEM_READ_ONLY
- if (f->mode != RDCF_READ) error_exit(f,RDCF_MODE_ERROR);
- #endif
- f->buffer_status = EMPTY;
- while (unread_bytes>0)
- {
- unsigned n = unread_bytes;
- unsigned rest_of_sector = SECTOR_SIZE - position%SECTOR_SIZE;
- unsigned sector;
- if (size<position+n) n = size-position;
- if (n==0) break;
- sector = first_sector_in_cluster(f, f->cluster) +
- (position/SECTOR_SIZE)%f->sectors_per_cluster;
- if (n>rest_of_sector) n = rest_of_sector;
- if (position%SECTOR_SIZE==0 && n==SECTOR_SIZE)
- read_sector(f, sector, buffer);
- else /* read a partial sector */
- {
- read_buffer(f, sector);
- memcpy(buffer, f->buffer+position%SECTOR_SIZE, n);
- }
- buffer += n;
- position += n;
- unread_bytes -= n;
- if (position%(f->sectors_per_cluster*SECTOR_SIZE)==0 && position<size)
- {
- unsigned next_cluster = FAT_entry(f, f->cluster);
- if (next_cluster >= f->last_cluster_mark) f->last_cluster = f->cluster;
- f->cluster = next_cluster;
- }
- }
- f->position = position;
- return f->result = count - unread_bytes;
- }
-
- #ifndef RDCF_SYSTEM_READ_ONLY
-
- int rdcf_recover(struct rdcf *f, char *spec)
- {
- if ((f->result=setjmp(f->error)) != 0) return f->result;
- if (find_file(f, initialize_fcb(f, spec)))
- error_exit(f,RDCF_ACCESS_DENIED);
- if (f->directory_index == NO_DIRECTORY_INDEX)
- error_exit(f,RDCF_DIRECTORY_FULL);
- f->file.attribute = RDCF_ARCHIVE;
- rdcf_get_date_and_time(&f->file.date_and_time);
- f->file.first_cluster = add_new_cluster(f, EMPTY_CLUSTER, 2);
- f->file.size = 0L;
- {
- unsigned cluster;
- for (cluster = f->file.first_cluster; cluster != EMPTY_CLUSTER;
- cluster = add_new_cluster(f, cluster, cluster))
- {
- f->file.size += f->sectors_per_cluster * SECTOR_SIZE;
- }
- }
- update_directory_entry(f, 0);
- flush_buffer(f);
- return 0;
- }
-
- #endif
-
- #ifndef RDCF_SYSTEM_READ_ONLY
-
- int rdcf_rename(struct rdcf *f, char *old_spec, char *new_spec)
- {
- unsigned char name_extension[NAME_SIZE+EXTENSION_SIZE];
- if ((f->result=setjmp(f->error)) != 0) return f->result;
- old_spec = initialize_fcb(f, old_spec);
- if (!find_file(f, old_spec))
- error_exit(f,RDCF_FILE_NOT_FOUND);
- if (f->file.attribute & (RDCF_HIDDEN+RDCF_SYSTEM))
- error_exit(f, RDCF_ACCESS_DENIED);
- spec_to_name_extension(f, name_extension, new_spec);
- f->directory_cluster = f->directory_first_cluster;
- if (find_file_in_directory_or_find_volume(f, name_extension))
- error_exit(f,RDCF_RENAMING_ERROR);
- find_file(f, old_spec);
- name_extension_to_spec(f->file.spec, name_extension);
- update_directory_entry(f, 0);
- flush_buffer(f);
- return 0;
- }
-
- #endif
-
- int rdcf_seek(struct rdcf *f, unsigned long offset)
- {
- unsigned i, cluster;
- if ((f->result=setjmp(f->error)) != 0) return f->result;
- if (offset>f->file.size) error_exit(f,RDCF_SEEK_OUT_OF_RANGE);
- f->buffer_status = EMPTY;
- cluster = f->file.first_cluster;
- for (i=offset/(f->sectors_per_cluster*SECTOR_SIZE); i>0; i--)
- {
- unsigned new_cluster = FAT_entry(f, cluster);
- if (new_cluster >= f->last_cluster_mark) f->last_cluster = cluster;
- cluster = new_cluster;
- }
- f->cluster = cluster;
- f->position = offset;
- return 0;
- }
-
- #ifndef RDCF_SYSTEM_READ_ONLY
-
- int rdcf_set_file_information(struct rdcf *f, char *spec, unsigned index,
- struct rdcf_file_information *file_information)
- {
- if ((f->result=setjmp(f->error)) != 0) return f->result;
- find_entry(f, spec, index);
- f->file = *file_information;
- if (f->file.spec[0] == 0)
- memset(find_directory(f), 0, sizeof(struct directory));
- else if (f->file.spec[0] == DELETED_FILE)
- {
- f->file.spec[0] = 'X';
- update_directory_entry(f, 1);
- }
- else
- update_directory_entry(f, 0);
- flush_buffer(f);
- return 0;
- }
-
- #endif
-
- #ifndef RDCF_SYSTEM_READ_ONLY
-
- int rdcf_set_volume(struct rdcf *f, char *spec)
- {
- unsigned char name_extension[NAME_SIZE+EXTENSION_SIZE];
- if ((f->result=setjmp(f->error)) != 0) return f->result;
- spec = initialize_fcb(f, spec);
- {
- unsigned i = 0;
- for (i = 0; i < sizeof(name_extension); i++)
- name_extension[i] = *spec != 0 ? *spec++ : ' ';
- }
- if (name_extension[0] == DELETED_FILE)
- error_exit(f, RDCF_INVALID_SPEC);
- find_file_in_directory_or_find_volume(f, NULL);
- if (f->directory_index == NO_DIRECTORY_INDEX)
- error_exit(f,RDCF_DIRECTORY_FULL);
- memcpy(f->file.spec, name_extension, sizeof(name_extension));
- rdcf_get_date_and_time(&f->file.date_and_time);
- f->file.attribute = RDCF_VOLUME;
- f->file.size = 0;
- f->file.first_cluster = 0;
- update_directory_entry(f, 0);
- flush_buffer(f);
- return 0;
- }
-
- #endif
-
- #ifndef RDCF_SYSTEM_READ_ONLY
-
- int rdcf_sort_directory(struct rdcf *f, char *spec, int mode)
- {
- int sorting = 1;
- unsigned first_index;
- unsigned number_of_directory_entries;
- if ((f->result=setjmp(f->error)) != 0) return f->result;
- find_entry(f, spec, 0);
- read_directory_entry(f);
- if (f->file.spec[0] == 0) return 0; /* empty directory */
- /* do not sort subdirectories . and .. or system files */
- while (f->file.spec[0] == '.' || f->file.spec[0] != 0 &&
- f->file.spec[0] != DELETED_FILE && f->file.attribute & RDCF_SYSTEM)
- {
- if (++f->directory_index > 2) error_exit(f, RDCF_FILE_FORMAT_ERROR);
- read_directory_entry(f);
- }
- if (f->file.spec[0] == 0) return 0;
- first_index = f->directory_index;
- number_of_directory_entries = f->directory_cluster == 0 ?
- (f->first_data_sector - f->first_directory_sector)*ENTRIES_PER_SECTOR :
- ENTRIES_PER_SECTOR*f->sectors_per_cluster;
- while (sorting)
- {
- struct directory *d, previous_entry;
- unsigned previous_directory_cluster;
- unsigned previous_directory_index;
- f->directory_index = first_index;
- f->directory_cluster = f->directory_first_cluster;
- sorting = 0;
- d = find_directory(f);
- while (1)
- {
- int comparison;
- previous_entry = *d;
- previous_directory_cluster = f->directory_cluster;
- previous_directory_index = f->directory_index;
- if (++f->directory_index == number_of_directory_entries)
- {
- if (f->directory_cluster == 0) break;
- {
- unsigned x = FAT_entry(f, f->directory_cluster);
- if (x >= f->last_cluster_mark) break;
- f->directory_cluster = x;
- f->directory_index = 0;
- }
- }
- d = find_directory(f);
- if (d->name_extension[0] == 0) break;
- comparison = entry_class(d) - entry_class(&previous_entry);
- if (comparison == 0)
- {
- switch (mode & RDCF_REVERSE-1)
- {
- case RDCF_EXTENSION_NAME:
- comparison =
- memcmp(d->name_extension+NAME_SIZE,
- previous_entry.name_extension+NAME_SIZE, EXTENSION_SIZE);
- if (comparison != 0) break;
- /* else fall through to case RDCF_NAME_EXTENSION */
- case RDCF_NAME_EXTENSION:
- comparison =
- memcmp(d->name_extension, previous_entry.name_extension,
- NAME_SIZE+EXTENSION_SIZE);
- break;
- case RDCF_DATE_TIME:
- comparison =
- compare_four_bytes((unsigned char *)(&d->time),
- (unsigned char *)(&previous_entry.time));
- break;
- case RDCF_SIZE:
- comparison =
- compare_four_bytes((unsigned char *)(&d->size),
- (unsigned char *)(&previous_entry.size));
- break;
- }
- if (mode & RDCF_REVERSE) comparison = -comparison;
- }
- if (comparison < 0)
- {
- struct directory this_entry = *d;
- unsigned this_directory_cluster = f->directory_cluster;
- unsigned this_directory_index = f->directory_index;
- f->directory_cluster = previous_directory_cluster;
- f->directory_index = previous_directory_index;
- d = find_directory(f);
- *d = this_entry;
- f->buffer_status = DIRTY;
- f->directory_cluster = this_directory_cluster;
- f->directory_index = this_directory_index;
- d = find_directory(f);
- *d = previous_entry;
- f->buffer_status = DIRTY;
- sorting = 1;
- }
- }
- }
- flush_buffer(f);
- return 0;
- }
-
- #endif
-
- #ifndef RDCF_SYSTEM_READ_ONLY
-
- int rdcf_undelete(struct rdcf *f, char *spec)
- {
- unsigned char name_extension[NAME_SIZE+EXTENSION_SIZE];
- int first_character;
- if ((f->result=setjmp(f->error)) != 0) return f->result;
- spec = initialize_fcb(f, spec);
- if (find_file(f, spec))
- {
- if (f->file.attribute & RDCF_DIRECTORY)
- error_exit(f, RDCF_DIRECTORY_CONFLICT);
- error_exit(f, RDCF_FILE_NOT_FOUND);
- }
- first_character = f->file.spec[0];
- spec = spec_to_name_extension(f, name_extension, f->file.spec);
- name_extension[0] = DELETED_FILE;
- f->directory_cluster = f->directory_first_cluster;
- if (!find_file_in_directory_or_find_volume(f, name_extension) ||
- f->file.attribute & RDCF_VOLUME+RDCF_DIRECTORY)
- {
- error_exit(f, RDCF_FILE_NOT_FOUND);
- }
- f->position = 0;
- if (f->file.size != 0)
- {
- unsigned cluster_size = SECTOR_SIZE * f->sectors_per_cluster;
- unsigned long recovered_size = cluster_size;
- unsigned cluster = f->file.first_cluster;
- if (FAT_entry(f, cluster) != EMPTY_CLUSTER)
- error_exit(f, RDCF_UNRECOVERABLE_FILE);
- set_FAT_entry(f, cluster, f->last_cluster_mark);
- f->file.spec[0] = first_character;
- {
- while (recovered_size < f->file.size &&
- cluster < f->maximum_cluster_number)
- {
- cluster = add_new_cluster(f, cluster, cluster+1);
- if (cluster == EMPTY_CLUSTER)
- {
- f->position = f->file.size - recovered_size;
- f->file.size = recovered_size;
- break;
- }
- recovered_size += cluster_size;
- }
- }
- }
- f->file.spec[0] = first_character;
- update_directory_entry(f, 0);
- flush_buffer(f);
- return 0;
- }
-
- #endif
-
- #ifndef RDCF_SYSTEM_READ_ONLY
-
- int rdcf_wipe_drive(struct rdcf *f
- #ifdef RDCF_MULTIPLE_DRIVE
- , char *spec
- #endif
- )
- {
- unsigned cluster;
- if ((f->result=setjmp(f->error)) != 0) return (long)(f->result);
- #ifndef RDCF_MULTIPLE_DRIVE
- initialize_fcb(f, NULL);
- #else
- if (*initialize_fcb(f, spec) != 0) error_exit(f, RDCF_INVALID_SPEC);
- #endif
- for (cluster = 2; cluster <= f->maximum_cluster_number; cluster++)
- {
- if (FAT_entry(f, cluster) == EMPTY_CLUSTER)
- {
- unsigned sector = first_sector_in_cluster(f, cluster);
- unsigned count = f->sectors_per_cluster;
- do write_sector(f, sector++, f->buffer); while (--count != 0);
- }
- }
- return 0;
- }
-
- #endif
-
- #ifndef RDCF_SYSTEM_READ_ONLY
-
- int rdcf_write(struct rdcf *f, void *buf, int count)
- {
- unsigned long size = f->file.size;
- unsigned long position = f->position;
- unsigned unwritten_bytes = count;
- unsigned first_possibly_empty_cluster = 2;
- char *buffer = buf;
- if ((f->result=setjmp(f->error)) != 0) return f->result;
- f->buffer_status = EMPTY;
- if (f->mode == RDCF_READ) error_exit(f,RDCF_MODE_ERROR);
- while (unwritten_bytes>0)
- {
- unsigned sector;
- unsigned n = unwritten_bytes;
- unsigned rest_of_sector = SECTOR_SIZE - position%SECTOR_SIZE;
- if (n>rest_of_sector) n = rest_of_sector;
- if (f->cluster == EMPTY_CLUSTER || f->cluster >= f->last_cluster_mark)
- {
- unsigned new_cluster =
- add_new_cluster(f, f->last_cluster, first_possibly_empty_cluster);
- if (new_cluster == EMPTY_CLUSTER) break;
- first_possibly_empty_cluster = new_cluster+1;
- f->cluster = f->last_cluster = new_cluster;
- if (f->file.first_cluster==EMPTY_CLUSTER)
- f->file.first_cluster = new_cluster;
- }
- sector = first_sector_in_cluster(f, f->cluster) +
- (position/SECTOR_SIZE)%f->sectors_per_cluster;
- if (position%SECTOR_SIZE==0 &&
- (n==SECTOR_SIZE || position+n>=size))
- {
- write_sector(f, sector, buffer);
- }
- else /* write a partial sector */
- {
- read_buffer(f, sector);
- memcpy(f->buffer+position%SECTOR_SIZE, buffer, n);
- f->buffer_status = DIRTY;
- }
- buffer += n;
- position += n;
- unwritten_bytes -= n;
- if (position>size) size = position;
- if (position%(f->sectors_per_cluster*SECTOR_SIZE)==0)
- {
- unsigned next_cluster = FAT_entry(f, f->cluster);
- if (next_cluster >= f->last_cluster_mark) f->last_cluster = f->cluster;
- f->cluster = next_cluster;
- }
- }
- flush_buffer(f);
- f->position = position;
- f->file.size = size;
- f->mode |= WRITTEN;
- return f->result = count-unwritten_bytes;
- }
-
- #endif
-
-